home *** CD-ROM | disk | FTP | other *** search
/ Night Owl 9 / Night Owl CD-ROM (NOPV9) (Night Owl Publisher) (1993).ISO / 003a / cpuid3wk.zip / CPUID3WK.ASM < prev    next >
Assembly Source File  |  1993-05-24  |  21KB  |  563 lines

  1. ;       Filename:       cpuid32.msm
  2. ;
  3. ;       This program has been developed by Intel Corporation.  You have
  4. ;       Intel's permission to incorporate this source code into your
  5. ;       product royalty free.
  6. ;
  7. ;       Intel specifically disclaims all warranties, express or implied,
  8. ;       and all liability, including consequential and other indirect
  9. ;       damages, for the use of this code, including liability for
  10. ;       infringement of any proprietary rights.  Intel does not assume
  11. ;       any responsibility for any errors which may appear in this code
  12. ;       nor any responsibility to update it.
  13. ;
  14. ;       This program contains three parts:
  15. ;       Part 1: Identifies CPU type in the variable cpu_type:
  16. ;               0=8086 processor
  17. ;               2=Intel 286 processor
  18. ;               3=Intel386(TM) processor
  19. ;               4=Intel486(TM) processor
  20. ;               5=Pentium(TM) processor
  21. ;
  22. ;       Part 2: Identifies FPU type in the variable fpu_type:
  23. ;               0=FPU not present
  24. ;               1=FPU present
  25. ;               2=287 present (only if cpu_type=3)
  26. ;               3=387 present (only if cpu_type=3)
  27. ;
  28. ;       Part 3: Prints out the appropriate message.  This part can
  29. ;               be removed if this program is not used in a DOS-based
  30. ;               system.  Portions affected are at the end of the
  31. ;               data segment and the print procedure in the code
  32. ;               segment.
  33. ;
  34. ;    REVISION HISTORY:
  35. ;       Date:  5/24/93
  36. ;       Modified by Wayne A. King, CIS: 70022,2700  CRS: WAYNE KING, to
  37. ;       eliminate 7 severe errors resulting from "Jump out of range"
  38. ;       conditions when assembled with MASM 5.1. Conditional jumps must be
  39. ;       SHORT for under-386 class machines. Reversed condition tests, adding
  40. ;       labels as required, and replaced offending NEAR conditional jumps
  41. ;       with unconditional JMPs.
  42. ;       N.B. - Author of this change is not affiliated in ANY way with Intel.
  43. ;
  44. ;       Date:  4/93
  45. ;    1. Replaced the .486 with .186 to avoid generation of 0FH type long
  46. ;       conditional branches (such as the branch to end_get_cpuid at
  47. ;       the end of the 8086/8088 test, near code address 003D).  These
  48. ;       are not executable on the 8086/8088 and 80286, so the code falls
  49. ;       into some strange place and hangs the system.  The .186 also allows
  50. ;       multi-bit shifts to unpack CPUID info.  Using .186 requires 
  51. ;       that all 32-bit operand prefix (66H) be inserted by hand.  
  52. ;       This is done with a macro (OPND32).
  53. ;    2. Avoid all usage of 32-bit operands until it is clear that the
  54. ;       CPU is at least an 80386.  The use of the Exx registers caused 
  55. ;       the generation of the 66H prefix, which are not executed 
  56. ;       correctly on the 8086/8088 and 80286.  
  57. ;    3. Eliminated all the register save/restore and added comment 
  58. ;       that all registers are used by the functions.
  59. ;    4. Do the stack alignment just before messing with the AC bit in
  60. ;       EFLAGS, otherwise the stack may not be properly aligned.  Also
  61. ;       restore the AC bit immediately, so it does not stay set.
  62. ;    5. Changed the FPU detection to set one flag fpu_type (instead of
  63. ;       the previous two flags: fpu_present and infinity).  An fpu_type
  64. ;       of zero indicates no floating point unit is present, an fpu_type
  65. ;       of 2 indicates an 80287 is present, an fpu_type of 3 indicates
  66. ;       an 80387 is present.
  67. ;
  68. ;    If this code is assembled with MASM with no options specified, it
  69. ;    runs correctly on an 8086/8088, 80286, 80386, 80486, and 
  70. ;    Pentium(tm) processor.
  71. ;
  72.  
  73.  
  74.         TITLE   CPUID
  75.         DOSSEG
  76.         .model  small
  77.         .stack  100h
  78.         .186
  79.  
  80. OPND32 MACRO op_code, op_erand
  81.         db      66h     ; Force 32-bit operand size
  82.   IFNB <op_code>
  83.         db      op_code
  84.     IFNB <op_erand>
  85.         dd      op_erand; 32-bit immediate value
  86.     ENDIF
  87.   ENDIF
  88. ENDM
  89.  
  90. CPUID MACRO
  91.         db      0fh     ; Hardcoded opcode for CPUID instruction
  92.         db      0a2h
  93. ENDM
  94.  
  95. TRUE            equ     1
  96. FAMILY_MASK     equ     0f00h
  97. FAMILY_SHIFT    equ     8
  98. MODEL_MASK      equ     0f0h
  99. MODEL_SHIFT     equ     4
  100. STEPPING_MASK   equ     0fh
  101. FPU_FLAG        equ     1h
  102. MCE_FLAG        equ     80h
  103. CMPXCHG8B_FLAG  equ     100h
  104.  
  105.         .data
  106. fp_status       dw      ?
  107. vendor_id       db      12 dup (?)
  108. cpu_type        db      ?
  109. model           db      ?
  110. stepping        db      ?
  111. id_flag         db      0
  112. fpu_type        db      0
  113. intel_proc      db      0
  114. feature_flags   dw      2 dup (0)
  115. ;
  116. ; remove the remaining data declarations if not using the DOS-based
  117. ; print procedure
  118. ;
  119. id_msg          db      "This system has a$"
  120. fp_8087         db      " and an 8087 math coprocessor$"
  121. fp_80287        db      " and an 80287 math coprocessor$"
  122. fp_80387        db      " and an 80387 math coprocessor$"
  123. c8086           db      "n 8086/8088 processor$"
  124. c286            db      "n 80286 processor$"
  125. c386            db      "n 80386 processor$"
  126. c486            db      "n 80486 DX processor or 80487 SX math coprocessor$"
  127. c486nfp         db      "n 80486 SX processor$"
  128. Intel486_msg    db      13,10,"This system contains a Genuine Intel486(TM) processor",13,10,"$"
  129. Pentium_msg     db      13,10,"This system contains a Genuine Intel Pentium(TM)  processor",13,10,"$"
  130. modelmsg        db      "Model:            $"
  131. steppingmsg     db      "Stepping:         $"
  132. familymsg       db      13,10,"Processor Family: $"
  133. period          db      ".",13,10,"$"
  134. dataCR          db      ?,13,10,"$"
  135. intel_id        db      "GenuineIntel"
  136. fpu_msg         db      13,10,"This processor contains a FPU",13,10,"$"
  137. mce_msg         db      "This processor supports the Machine Check Exception",13,10,"$"
  138. cmp_msg         db      "This processor supports the CMPXCHG8B instruction",13,10,"$"
  139. not_intel       db      "t least an 80486 processor.",13,10
  140.                 db      "It does not contain a Genuine Intel part and as a result,",13,10
  141.                 db      "the CPUID detection information cannot be determined at this time.",13,10,"$"
  142.  
  143. ;
  144. ;       The purpose of this code is to identify the processor and
  145. ;       coprocessor that is currently in the system.  The program first
  146. ;       determines the processor id.  When that is accomplished,
  147. ;       the program then determines whether a coprocessor
  148. ;       exists in the system.  If a coprocessor or integrated
  149. ;       coprocessor exists, the program identifies
  150. ;       the coprocessor id.  The program then prints out
  151. ;       the CPU and floating point presence and type.
  152. ;
  153.         .code
  154. start:  mov     ax, @data
  155.         mov     ds, ax          ; set segment register
  156.         mov     es, ax          ; set segment register
  157.         pushf                   ; save for restoration at end
  158.         call    get_cpuid
  159.         call    get_fpuid
  160.         call    print
  161.         popf
  162.         mov     ax, 4c00h       ; terminate program
  163.         int     21h
  164.  
  165.  
  166. get_cpuid proc
  167. ;
  168. ;       This procedure determines the type of CPU in a system
  169. ;       and sets the cpu_type variable with the appropriate
  170. ;       value.
  171. ;       All registers are used by this procedure, none are preserved.
  172.  
  173. ;       Intel 8086 CPU check
  174. ;       Bits 12-15 of the FLAGS register are always set on the
  175. ;       8086 processor.
  176. ;
  177. check_8086:
  178.         pushf                   ; push original FLAGS
  179.         pop     ax              ; get original FLAGS
  180.         mov     cx, ax          ; save original FLAGS
  181.         and     ax, 0fffh       ; clear bits 12-15 in FLAGS
  182.         push    ax              ; save new FLAGS value on stack
  183.         popf                    ; replace current FLAGS value
  184.         pushf                   ; get new FLAGS
  185.         pop     ax              ; store new FLAGS in AX
  186.         and     ax, 0f000h      ; if bits 12-15 are set, then CPU
  187.         cmp     ax, 0f000h      ;   is an 8086/8088
  188.         mov     cpu_type, 0     ; turn on 8086/8088 flag
  189.         jne     check_80286
  190.         jmp     end_get_cpuid   ; jump if CPU is 8086/8088
  191.  
  192. ;       Intel 286 CPU check
  193. ;       Bits 12-15 of the FLAGS register are always clear on the
  194. ;       Intel 286 processor in real-address mode.
  195. ;
  196. check_80286:
  197.         or      cx, 0f000h      ; try to set bits 12-15
  198.         push    cx              ; save new FLAGS value on stack
  199.         popf                    ; replace current FLAGS value
  200.         pushf                   ; get new FLAGS
  201.         pop     ax              ; store new FLAGS in AX
  202.         and     ax, 0f000h      ; if bits 12-15 clear, CPU=80286
  203.         mov     cpu_type, 2     ; turn on 80286 flag
  204.         jnz     check_80386
  205.         jmp     end_get_cpuid   ; if no bits set, CPU is 80286
  206.  
  207. ;       Intel386 CPU check
  208. ;       The AC bit, bit #18, is a new bit introduced in the EFLAGS
  209. ;       register on the Intel486 DX CPU to generate alignment faults.
  210. ;       This bit cannot be set on the Intel386 CPU.
  211. ;
  212. check_80386:
  213. ;       It is now safe to use 32-bit opcode/operands
  214.         mov     bx, sp          ; save current stack pointer to align
  215.         and     sp, not 3       ; align stack to avoid AC fault
  216.         OPND32
  217.         pushf                   ; push original EFLAGS
  218.         OPND32
  219.         pop     ax              ; get original EFLAGS
  220.         OPND32
  221.         mov     cx, ax          ; save original EFLAGS
  222.         OPND32  35h, 40000h     ; flip AC bit in EFLAGS
  223.         OPND32
  224.         push    ax              ; save new EFLAGS value on stack
  225.         OPND32
  226.         popf                    ; replace current EFLAGS value
  227.         OPND32
  228.         pushf                   ; get new EFLAGS
  229.         OPND32
  230.         pop     ax              ; store new EFLAGS in EAX
  231.         OPND32
  232.         xor     ax, cx          ; can't toggle AC bit, CPU=80386
  233.         mov     cpu_type, 3     ; turn on 80386 CPU flag
  234.         mov     sp, bx          ; restore original stack pointer
  235.         jz      end_get_cpuid   ; jump if 80386 CPU
  236.         and     sp, not 3       ; align stack to avoid AC fault
  237.         OPND32
  238.         push    cx
  239.         OPND32
  240.         popf                    ; restore AC bit in EFLAGS first
  241.         mov     sp, bx          ; restore original stack pointer
  242.  
  243. ;       Intel486 DX CPU, Intel487 SX NDP, and Intel486 SX CPU check
  244. ;       Checking for ability to set/clear ID flag (Bit 21) in EFLAGS
  245. ;       which indicates the presence of a processor
  246. ;       with the ability to use the CPUID instruction.
  247. ;
  248. check_80486:
  249.         mov     cpu_type, 4     ; turn on 80486 CPU flag
  250.         OPND32
  251.         mov     ax, cx          ; get original EFLAGS
  252.         OPND32  35h, 200000h    ; flip ID bit in EFLAGS
  253.         OPND32
  254.         push    ax              ; save new EFLAGS value on stack
  255.         OPND32
  256.         popf                    ; replace current EFLAGS value
  257.         OPND32
  258.         pushf                   ; get new EFLAGS
  259.         OPND32
  260.         pop     ax              ; store new EFLAGS in EAX
  261.         OPND32
  262.         xor     ax, cx          ; can't toggle ID bit,
  263.         je      end_get_cpuid   ;   CPU=80486
  264.  
  265. ;       Execute CPUID instruction to determine vendor, family,
  266. ;       model and stepping.
  267. ;
  268. check_vendor:
  269.         mov     id_flag, 1              ; set flag indicating use of CPUID inst.
  270.         OPND32
  271.         xor     ax, ax                  ; set up input for CPUID instruction
  272.         CPUID                           ; macro for CPUID instruction
  273.         OPND32
  274.         mov     word ptr vendor_id, bx  ; setup to test for vendor id
  275.         OPND32
  276.         mov     word ptr vendor_id[+4], dx
  277.         OPND32
  278.         mov     word ptr vendor_id[+8], cx
  279.         mov     si, offset vendor_id
  280.         mov     di, offset intel_id
  281.         mov     cx, length intel_id
  282. compare:
  283.         repe    cmpsb                   ; compare vendor id to "GenuineIntel"
  284.         or      cx, cx
  285.         jnz     end_get_cpuid           ; if not zero, not an Intel CPU,
  286.  
  287. intel_processor:
  288.         mov     intel_proc, 1
  289.  
  290. cpuid_data:
  291.         OPND32
  292.         cmp     ax, 1                   ; make sure 1 is a valid input
  293.                                         ; value for CPUID
  294.         jl      end_get_cpuid           ; if not, jump to end
  295.         OPND32
  296.         xor     ax, ax                  ; otherwise, use as input to CPUID
  297.         OPND32
  298.         inc     ax                      ; and get stepping, model and family
  299.         CPUID
  300.         mov     stepping, al
  301.         and     stepping, STEPPING_MASK ; isolate stepping info
  302.  
  303.         and     al, MODEL_MASK          ; isolate model info
  304.         shr     al, MODEL_SHIFT
  305.         mov     model, al
  306.  
  307.         and     ax, FAMILY_MASK         ; mask everything but family
  308.         shr     ax, FAMILY_SHIFT
  309.         mov     cpu_type, al            ; set cpu_type with family
  310.  
  311.         OPND32
  312.         mov     feature_flags, dx       ; save feature flag data
  313.  
  314. end_get_cpuid:
  315.         ret
  316. get_cpuid endp
  317.  
  318.  
  319. ;******************************************************************
  320.  
  321. get_fpuid proc
  322. ;
  323. ;       This procedure determines the type of FPU in a system
  324. ;       and sets the fpu_type variable with the appropriate
  325. ;       value.
  326. ;       All registers are used by this procedure, none are preserved.
  327.  
  328. ;       Coprocessor check
  329. ;       The algorithm is to determine whether the floating-point
  330. ;       status and control words can be written to.  If not, no
  331. ;       coprocessor exists.  If the status and control words can be
  332. ;       written to, the correct coprocessor is then determined
  333. ;       depending on the processor id.  The Intel386 CPU can
  334. ;       work with either an Intel287 NDP or an Intel387 NDP.
  335. ;       The infinity of the coprocessor must be
  336. ;       checked to determine the correct coprocessor id.
  337.  
  338.         fninit                  ; reset FP status word
  339.         mov     fp_status, 5a5ah; initialize temp word to
  340.                                 ; non-zero value
  341.         fnstsw  fp_status       ; save FP status word
  342.         mov     ax, fp_status   ; check FP status word
  343.         cmp     al, 0           ; see if correct status with
  344.                                 ; written
  345.         mov     fpu_type, 0     ; no fpu present
  346.         jne     end_get_fpuid
  347.  
  348. check_control_word:
  349.         fnstcw  fp_status       ; save FP control word
  350.         mov     ax, fp_status   ; check FP control word
  351.         and     ax, 103fh       ; see if selected parts
  352.                                 ; looks OK
  353.         cmp     ax, 3fh         ; check that 1's & 0's
  354.                                 ; correctly read
  355.         mov     fpu_type, 0
  356.         jne     end_get_fpuid
  357.         mov     fpu_type, 1
  358.  
  359. ;
  360. ;   80287/80387 check for the Intel386 CPU
  361. ;
  362. check_infinity:
  363.         cmp     cpu_type, 3
  364.         jne     end_get_fpuid
  365.         fld1                    ; must use default control from FNINIT
  366.         fldz                    ; form infinity
  367.         fdiv                    ; 8087 and Intel287 NDP say +inf = -inf
  368.         fld     st              ; form negative infinity
  369.         fchs                    ; Intel387 NDP says +inf <> -inf
  370.         fcompp                  ; see if they are the same and remove them
  371.         fstsw   fp_status       ; look at status from FCOMPP
  372.         mov     ax, fp_status
  373.         mov     fpu_type, 2     ; store Intel287 NDP for fpu type
  374.         sahf                    ; see if infinities matched
  375.         jz      end_get_fpuid   ; jump if 8087 or Intel287 is present
  376.         mov     fpu_type, 3     ; store Intel387 NDP for fpu type
  377. end_get_fpuid:
  378.         ret
  379. get_fpuid endp
  380.  
  381.  
  382. ;*********************************************************************
  383.  
  384. print proc
  385. ;
  386. ;       This procedure prints the appropriate cpuid string and
  387. ;       numeric processor presence status.  If the CPUID instruction
  388. ;       was supported, this procedure prints out cpuid info.
  389. ;       All registers are used by this procedure, none are preserved.
  390.  
  391.         cmp     id_flag, 1              ; if set to 1, cpu supports
  392.                                         ;   CPUID instruction
  393.                                         ; print detailed CPUID information
  394.         jne     print_init
  395.         jmp     print_cpuid_data
  396.  
  397. print_init:
  398.         mov     dx, offset id_msg       ; print initial message
  399.         mov     ah, 9h
  400.         int     21h
  401.  
  402. print_86:
  403.         cmp     cpu_type, 0
  404.         jne     print_286
  405.         mov     dx, offset c8086
  406.         mov     ah, 9h
  407.         int     21h
  408.         cmp     fpu_type, 0
  409.         jne     print_86a
  410.         jmp     end_print
  411.  
  412. print_86a:
  413.         mov     dx, offset fp_8087
  414.         mov     ah, 9h
  415.         int     21h
  416.         jmp     end_print
  417.  
  418. print_286:
  419.         cmp     cpu_type, 2
  420.         jne     print_386
  421.         mov     dx, offset c286
  422.         mov     ah, 9h
  423.         int     21h
  424.         cmp     fpu_type, 0
  425.         jne     print_286a
  426.         jmp     end_print
  427.  
  428. print_286a:
  429.         mov     dx, offset fp_80287
  430.         mov     ah, 9h
  431.         int     21h
  432.         jmp     end_print
  433.  
  434. print_386:
  435.         cmp     cpu_type, 3
  436.         jne     print_486
  437.         mov     dx, offset c386
  438.         mov     ah, 9h
  439.         int     21h
  440.         cmp     fpu_type, 0
  441.         jne     print_386a
  442.         jmp     end_print
  443.  
  444. print_386a:
  445.         cmp     fpu_type, 2
  446.         jne     print_387
  447.         mov     dx, offset fp_80287
  448.         mov     ah, 9h
  449.         int     21h
  450.         jmp     end_print
  451.  
  452. print_387:
  453.         mov     dx, offset fp_80387
  454.         mov     ah, 9h
  455.         int     21h
  456.         jmp     end_print
  457.  
  458. print_486:
  459.         cmp     fpu_type, 0
  460.         je      print_Intel486sx
  461.         mov     dx, offset c486
  462.         mov     ah, 9h
  463.         int     21h
  464.         jmp     end_print
  465.  
  466. print_Intel486sx:
  467.         mov     dx, offset c486nfp
  468.         mov     ah, 9h
  469.         int     21h
  470.         jmp     end_print
  471.  
  472. print_cpuid_data:
  473.  
  474. cmp_vendor:
  475.         cmp     intel_proc, 1
  476.         je      is_GenuineIntel
  477.         jmp     not_GenuineIntel
  478.  
  479. is_GenuineIntel:
  480.         cmp     cpu_type, 4                     ; if cpu_type=4, print
  481.                                                 ; Intel486 CPU message
  482.         jne     check_Pentium
  483.         mov     dx, offset Intel486_msg
  484.         mov     ah, 9h
  485.         int     21h
  486.         jmp     print_family
  487.  
  488. check_Pentium:
  489.         cmp     cpu_type, 5                     ; if cpu_type=5, print
  490.         jne     print_features                  ; Pentium processor message
  491.         mov     dx, offset Pentium_msg
  492.         mov     ah, 9h
  493.         int     21h
  494.  
  495. print_family:
  496.         mov     dx, offset familymsg            ; print family msg
  497.         mov     ah, 9h
  498.         int     21h
  499.         mov     al, cpu_type
  500.         mov     byte ptr dataCR, al
  501.         add     byte ptr dataCR, 30h            ; convert to ASCII
  502.         mov     dx, offset dataCR               ; print family info
  503.         mov     ah, 9h
  504.         int     21h
  505.  
  506. print_model:
  507.         mov     dx, offset modelmsg             ; print model msg
  508.         mov     ah, 9h
  509.         int     21h
  510.         mov     al, model
  511.         mov     byte ptr dataCR, al
  512.         add     byte ptr dataCR, 30h            ; convert to ASCII
  513.         mov     dx, offset dataCR               ; print model info
  514.         mov     ah, 9h
  515.         int     21h
  516.  
  517. print_stepping:
  518.         mov     dx, offset steppingmsg          ; print stepping msg
  519.         mov     ah, 9h
  520.         int     21h
  521.         mov     al, stepping
  522.         mov     byte ptr dataCR, al
  523.         add     byte ptr dataCR, 30h            ; convert to ASCII
  524.         mov     dx, offset dataCR               ; print stepping info
  525.         mov     ah, 9h
  526.         int     21h
  527.  
  528. print_features:
  529.         mov     ax, feature_flags
  530.         and     ax, FPU_FLAG                    ; check for FPU
  531.         jz      check_MCE
  532.         mov     dx, offset fpu_msg
  533.         mov     ah, 9h
  534.         int     21h
  535.  
  536. check_MCE:
  537.         mov     ax, feature_flags
  538.         and     ax, MCE_FLAG                    ; check for MCE
  539.         jz      check_CMPXCHG8B
  540.         mov     dx, offset mce_msg
  541.         mov     ah, 9h
  542.         int     21h
  543.  
  544. check_CMPXCHG8B:
  545.         mov     ax, feature_flags
  546.         and     ax, CMPXCHG8B_FLAG              ; check for CMPXCHG8B
  547.         jz      end_print
  548.         mov     dx, offset cmp_msg
  549.         mov     ah, 9h
  550.         int     21h
  551.         jmp     end_print
  552.  
  553. not_GenuineIntel:
  554.         mov     dx, offset not_Intel
  555.         mov     ah, 9h
  556.         int     21h
  557.  
  558. end_print:
  559.         ret
  560. print endp
  561.  
  562.         end     start
  563.